package mylogger

import (
	"fmt"
	"os"
	"path"
	"time"
)

var (
	MaxSizeChan = 50000	// 日志通道缓冲区的大小
)
type FileLogger struct {
	Level       LogLevel
	filePath    string // 日志文件保存的路径
	fileName    string // 日志文件的文件名
	maxFileSize int64  // 最大文件大小超过则拆分日志
	// file slice by date
	// "y" Log files are cut through year
	// "m" Log files are cut through mouth
	// "d" Log files are cut through day
	// "h" Log files are cut through hour
	//dateSlice string	// 根据传入的日期来分割日志，默认按天分割
	startTime int64
	fileObj     *os.File
	errFileObj  *os.File
	logChan		chan *logMsg	// 后台写日志的通道
}

type logMsg struct {
	level		LogLevel
	msg 		string
	funcName	string
	fileName	string
	timestamp	string
	line 		int
}

// 构造函数
func NewFileLogger(levelStr, fp, fn string, maxSize int64) *FileLogger {
	f1 := &FileLogger{
		Level:       parseLogLevel(levelStr),
		filePath:    fp,
		fileName:    fn,
		maxFileSize: maxSize,
		//dateSlice: dateSlice,
		logChan: make(chan *logMsg,MaxSizeChan),
	}
	err := f1.initFile() // 按照文件路径和文件名将文件打开
	if err != nil {
		panic(err)
	}
	return f1
}

// 根据指定的日志文件路径和文件名打开日志文件
func (f *FileLogger) initFile() error {
	fullFileName := path.Join(f.filePath, f.fileName)
	fileObj, err1 := os.OpenFile(fullFileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err1 != nil {
		fmt.Printf("open log file failed, err:%v\n", err1)
		return err1
	}
	errFileObj, err2 := os.OpenFile(fullFileName+".err", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err2 != nil {
		fmt.Printf("open log file failed, err:%v\n", err2)
		return err2
	}
	f.startTime = time.Now().Unix()
	f.fileObj = fileObj
	f.errFileObj = errFileObj

	// 开启一个后台的goroutine去往文件中写日志
	go f.writeLogBackground()
	return nil
}

// 检查日志大小，根据日志大小分割日志
func (f *FileLogger) checkSize(file *os.File) bool{
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("get file info failed, err:%v\n", err)
		return false
	}
	// 如果当前文件大小大于等于设置的日志文件最大值
	return fileInfo.Size() >= f.maxFileSize
}
// 检查时间，根据时间分割日志
func (f *FileLogger) checkDate() bool{
	startTime := time.Unix(f.startTime, 0)
	nowTime := time.Now()
	// 默认安装天来切割日志，可根据日志量更改此处代码
	//return startTime.Format("20060102") != nowTime.Format("20060102")
	return startTime.Format("20060102") != nowTime.Format("20060102")
	//if (f.dateSlice == "y") && (startTime.Year() != nowTime.Year()) {
	//	return  startTime.Format("2006")
	//} else if (f.dateSlice == "m") && (startTime.Format("200601") != nowTime.Format("200601")) {
	//	return startTime.Format("200601")
	//} else if (f.dateSlice == "d") &&
	//	(startTime.Format("20060102") != nowTime.Format("20060102")) {
	//	return startTime.Format("20060102")
	//} else if (f.dateSlice == "h") &&
	//	(startTime.Format("2006010215") != startTime.Format("2006010215")) {
	//	return startTime.Format("2006010215")
	//}else {
	//	return startTime.Format("20060102")
	//}
	// 如果当前文件大小大于等于设置的日志文件最大值
}



// 切割日志
func (f *FileLogger) splitFile(file *os.File) (*os.File, error) {
	// 需要切割日志文件
	nowStr := time.Now().Format("20060102150405000")
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("get fileInfo failed, err:%v\n", err)
		return nil, err
	}
	logName := path.Join(f.filePath, fileInfo.Name())
	newLogName := fmt.Sprintf("%s.bak%s", logName, nowStr)
	// 1、关闭当前的日志文件
	file.Close()
	// 2、备份日志文件 rname xx.log -> xx.log.bak202103260947
	os.Rename(logName, newLogName)
	// 3、打开一个新的日志文件
	fileObj, err := os.OpenFile(logName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Printf("open new log file failed, err:%v\n", err)
		return nil, err
	}
	// 日志分割之后，需要将当前时间赋给startTime
	f.startTime=time.Now().Unix()
	// 4、将打开的新的日志文件对象复制给f.fileObj
	return fileObj, nil
}

// 后台写日志的方法
func (f *FileLogger) writeLogBackground(){
	// 死循环，一直从通道中取值，往日志文件中写
	for{
		if f.checkSize(f.fileObj) || f.checkDate(){
			newFileObj,err:=f.splitFile(f.fileObj)
			if err!=nil{
				return
			}
			f.fileObj=newFileObj
		}
		select {
		case logTmp:=<-f.logChan:	// 避免没有日志，取不出来日志时阻塞
			// 把日志先拼出来
			logMessage:=fmt.Sprintf("[%s] [%s] [%s:%s:%d] %s\n", logTmp.timestamp, levelToStr(logTmp.level), logTmp.fileName, logTmp.funcName, logTmp.line, logTmp.msg)
			fmt.Fprintf(f.fileObj,logMessage )
			if logTmp.level >= ERROR {
				if f.checkSize(f.errFileObj){
					newFileObj,err:=f.splitFile(f.errFileObj)
					if err!=nil{
						return
					}
					f.errFileObj=newFileObj
				}
				fmt.Fprintf(f.errFileObj, logMessage)
			}
		default:
			// 取不到日志先休息500毫秒
			time.Sleep(time.Millisecond*500)
		}
	}
}

// 记录日志的方法
func (f *FileLogger) log(level LogLevel, format string, a ...interface{}) {
	if f.compare(level) {
		msg := fmt.Sprintf(format, a...)
		now := time.Now()
		funcName, fileName, lineNum := getInfo(3)
		// 先把日志发送到通道中
		// 1.造一个logMsg对象
		logTmp:=&logMsg{
			level:level,
			msg:msg,
			funcName:funcName,
			fileName:fileName,
			timestamp:now.Format("2006-01-02,15:04:05"),
			line:lineNum,
		}
		select {
		case f.logChan<-logTmp:	// 日志写满之后会阻塞，需要处理
		default:
			// 什么都不干，把日志丢掉了，以保证不出现阻塞
		}

	}
}

// 根据日志的级别判断是否需要记录日志
func (f *FileLogger) compare(level LogLevel) bool {
	return level >= f.Level
}

func (f *FileLogger) Debug(msg string, a ...interface{}) {
	f.log(DEBUG, msg, a...)
}

func (f *FileLogger) Info(msg string, a ...interface{}) {
	f.log(INFO, msg, a...)
}

func (f *FileLogger) Warning(msg string, a ...interface{}) {
	f.log(WARNING, msg, a...)
}

func (f *FileLogger) Error(msg string, a ...interface{}) {
	f.log(ERROR, msg, a...)
}

func (f *FileLogger) Fatal(msg string, a ...interface{}) {
	f.log(FATAL, msg, a...)
}

func (f *FileLogger) Closes() {
	f.fileObj.Close()
	f.errFileObj.Close()
}
